from flask import Flask,url_for,request,redirect,abort,make_response,json,jsonify,session,g
import click
from datetime import datetime
import time
from jinja2.utils import generate_lorem_ipsum
from markupsafe import escape
app = Flask(__name__)

#配置
app.config["AUTHOR"] = "caoyx"

#下面两种方式都可以设置密钥
app.secret_key = "SDFSDKLJFKJ#J+$@#492194=1"
#app.config["SECRET_KEY"] = "SDFSDKLJFKJ#J+$@#492194=1"

app.config.update(VERSION='1.0.0',DESC="多个配置参数设置演示")

#自定义命令
@app.cli.command()
@click.argument("name")
def say_hello(name: str):
    """
    @app.cli.command('hello')
    通过 > flask hello jack 接口访问到
    @app.cli.command()
    通过函数名返回,注意下划线变中划线
    > flask say-hello jack
    :return:
    """
    click.echo(f"hello,{name}")


@app.route("/")
@app.route("/hello")
def index():

    # name = request.args.get("name")
    # if not name:
    #     name = request.cookies.get("name","Flask")
    name = g.name
    auth = "未登录"
    if session.get('logined'):
        auth = "已登录"
    return f"<html><head><title>Demo</title></head><body><div>Hello {name} <span>[{auth}]</span></div></body></html>"

#允许使用默认参数
@app.route("/greet",defaults={"name":"默认值"})
@app.route("/greet/<name>")
def greet(name):
    return f"来自请求 {url_for(endpoint='greet',name=name,_external=True)} 的响应 <div>hello {name}</div>"


#url和端点
@app.route("/endpoint",endpoint="my_endpoint")
def url_endpoint():
    return url_for("my_endpoint")

@app.route("/print")
def print_info():
    print(request.args.get("name",None))
    return f"<div>request.path = {request.path}</div>" \
           f"<div>request.full_path = {request.full_path}</div>"\
           f"<div>request.query_string = {request.query_string}</div>" \
           f"<div>request.args = {request.args}</div>" \
           f"<div>request.host = {request.host}</div>" \
           f"<div>request.host_url = {request.host_url}</div>"\
           f"<div>request.base_url = {request.base_url}</div>"\
           f"<div>request.url = {request.url}</div>"\
           f"<div>request.blueprint = {request.blueprint}</div>"\
           f"<div>request.endpoint = {request.endpoint}</div>"\
           f"<div>request.data = {request.data}</div>"\
           f"<div>request.cookies = {request.cookies}</div>"\


# 路由表
@app.route("/url_map")
def print_routemap():
    print(app.url_map)
    s = ""
    for r in app.url_map.iter_rules():
        s += f" r.methods={r.methods}, r.endpoint={r.endpoint} <br>"
    return s


# GET 请求会收到 405 Method Not Allowed
@app.route('/only_post',methods=["POST"])
def only_post():
    return "only post"

#url 处理,转换器,如果不符合数据格式 返回404
@app.route("/goback/<int:year>")
def goback(year: int):
    return f"welcome to {datetime.today().year - year}"

@app.route("/select/<any('啤酒','烤鸡','coffee'):good>")
def select_food(good: str):
    return f"您选择了{good}"

#请求钩子

@app.before_first_request
def init_system():
    print("系统第一个请求来时执行，一般用来做系统初始化之类的操作")

@app.before_request
def pre_process():
    print("每个请求都会打印",request.url)
    g.name = request.args.get("name") if request.args.get("name") else request.cookies.get("name","Flask")


@app.after_request
def commit_db(resp):
    print("每个请求执行完毕后执行,一般可以用于提交数据库")
    return resp

@app.teardown_request
def do_finally(resp):
    print("无论是否有异常,每个请求后会被调用")
    return resp
@app.route("/raise")
def raise_error():
    raise Exception("人为抛出异常")


#响应
@app.route("/to_baidu")
def to_baidu():
    """
    返回三个元素元组  响应主体,状态码,头部字段
    :return:
    """
    return '',302,{'Location':"https://www.baidu.com"}

@app.route("/to_17dz")
def to_17dz():
    return redirect("https://www.17dz.com")

@app.route("/hi")
def hi():
    return redirect(url_for('greet'))


#错误响应
@app.route("/abort")
def abort_error():
    #abort（）函数前不需要使用return语句，但一旦abort（）函数被调用，abort（）函数之后的代码将不会被执行
    abort(418,"要啥自行车")

#响应格式
@app.route("/json")
def get_json():
    # make_response 来定制响应格式
    data = {
        "name":"马大帅",
        "age":44
    }
    resp = make_response(json.dumps(data))
    #resp.headers['Content-Type'] = 'application/json;charset=UTF-8'
    #设置 mimetype 更简洁一些
    resp.mimetype = "application/json"

    return resp
    #return jsonify(name="马大帅",age=44)

# cookie

@app.route("/set/<string:name>")
def set_name(name: str):
    resp = make_response(redirect(url_for("index")))
    resp.set_cookie("name",name)
    return resp;

@app.route("/login")
def login():
    session['logined']=True
    session['id']="123456"
    return redirect(url_for("index"))

@app.route("/logout")
def logout():
    if "logined" in session:
        session.pop("logined")
    return redirect(url_for("index"))

#跳回原连接
@app.route("/shopping")
def shopping():
    return f"<h1>商品页面</h1><a href=\'{url_for('pay')}\'>支付</a>"

@app.route("/pay")
def pay():
    time.sleep(2)
    print("支付完成")
    #request.referrer 有可能为空
    return redirect(request.referrer or url_for("shopping"))


@app.route('/more')
def load_post():
    return generate_lorem_ipsum(n=1)


@app.route('/post')
def show_post():
    post_body = generate_lorem_ipsum(n=2) # 生成两段随机文本
    return '''
    <h1>A very long post</h1>
    <div class="body">%s</div>
    <button id="load">Load More</button>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script type="text/javascript">
        $(function() {
            $('#load').click(function() {
                $.ajax({
                url: '/more', // 目标URL
                type: 'get', // 请求方法
                success: function(data){ // 返回2XX响应后触发的回调函数
                        $('.body').append(data); // 将返回的响应插入到页面中
                    }
                })
            })
        })
    </script>''' % post_body


@app.route("/xss")
def xss():
    """
    http://127.0.0.1:5000/xss?name=<script>alert('ok');</script>
    访问这个地址将回被XSS攻击，使用 escape 进行转义
    """
    name = request.args.get("name")

    #return f"<h3>hello,{name}</h3>"
    return f"<h3>hello,{escape(name)}</h3>"

if __name__ == '__main__':
    app.run(debug=True)